-module(make_fun).
-export([nar/2, from_text/2, inverse/1]).

%%%%% narity
nar(F, Arity) ->
    case Arity of   % due to optimization considerations
    1 -> 
        fun( X1 ) -> F([ X1 ]) end;
    2 -> 
        fun( X1, X2 ) -> F([ X1, X2 ]) end;
    3 -> 
        fun( X1, X2, X3 ) -> F([ X1, X2,X3 ]) end;
    4 -> 
        fun( X1, X2, X3, X4 ) -> F([ X1, X2, X3, X4 ]) end;
    5 -> 
        fun( X1, X2, X3, X4, X5 ) -> F([ X1, X2, X3, X4, X5 ]) end;
    _ ->
        Xs = string:join(["X" ++ integer_to_list(I) || I <- lists:seq(1, Arity)], ", "),
        FunStr = "fun (" ++ Xs ++ ") -> F([" ++ Xs ++ "]) end.", 
        {ok, Tokens, _} = erl_scan:string(FunStr),
        {ok, [Form]} = erl_parse:parse_exprs(Tokens),
        Bindings = erl_eval:add_binding('F', F, erl_eval:new_bindings()), 
        {value, Fa, _} = erl_eval:expr(Form, Bindings),
        Fa
    end.

from_text(Text, Arity) ->
    Xs = string:join(["X" ++ integer_to_list(I) || I <- lists:seq(1, Arity)], ", "),
    FunStr = "fun (" ++ Xs ++ ") -> " ++ Text ++ " end.", 
    {ok, Tokens, _} = erl_scan:string(FunStr),
    {ok, [Form]} = erl_parse:parse_exprs(Tokens),
    {value, Fa, _} = erl_eval:expr(Form, erl_eval:new_bindings()),
    Fa.

inverse(F) ->
    Arity = proplists:get_value( arity, erlang:fun_info( F )),
    Xs = string:join(["X" ++ integer_to_list(I) || I <- lists:seq(1, Arity)], ", "),
    FunStr = "fun (" ++ Xs ++ ") -> - F("++ Xs ++") end.", 
    {ok, Tokens, _} = erl_scan:string(FunStr),
    {ok, [Form]} = erl_parse:parse_exprs(Tokens),
    Bindings = erl_eval:add_binding('F', F, erl_eval:new_bindings()), 
    {value, Fa, _} = erl_eval:expr(Form, Bindings),
    Fa.
